package com.ruicar.afs.cloud.filecenter.service.impl.processor;

import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruicar.afs.cloud.common.core.constant.CommonConstants;
import com.ruicar.afs.cloud.common.core.exception.AfsBaseException;
import com.ruicar.afs.cloud.filecenter.config.FileProperties;
import com.ruicar.afs.cloud.filecenter.entity.AfsFileInfo;
import com.ruicar.afs.cloud.filecenter.entity.AfsFileStore;
import com.ruicar.afs.cloud.filecenter.enums.ProcessStatus;
import com.ruicar.afs.cloud.filecenter.enums.ResultCodeEnum;
import com.ruicar.afs.cloud.filecenter.properties.GwtFastDfsProperties;
import com.ruicar.afs.cloud.filecenter.service.AfsFileProcessor;
import com.ruicar.afs.cloud.filecenter.service.IFileInfoService;
import com.ruicar.afs.cloud.filecenter.service.IFileStoreService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import java.io.File;
import java.text.MessageFormat;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Component
@Slf4j
@AllArgsConstructor
public class FinishToOssProcessor implements AfsFileProcessor {
    private static final TokenHolder TOKEN_HOLDER = new TokenHolder();
    private final FileProperties fileProperties;
    /**
     * Ifile store service
     * <p>Description: </p>
     */
    private final IFileStoreService ifileStoreService;
    /**
     * File info service
     * <p>Description: </p>
     */
    private final IFileInfoService fileInfoService;


    private final GwtFastDfsProperties gwtFastdfsProperties;


    private static String URL_TEMPLATE = "{0}/S120017/v2/upload?md5={1}&sceneid={2}&randomstr={3}";

    @Override
    public String registerId() {
        return "up-file-to-oss";
    }


    @Override
    public void process(String fileMd5,String guid) {
        AfsFileStore afsFileStore =  ifileStoreService.getOne(Wrappers.<AfsFileStore>lambdaQuery().eq(AfsFileStore::getMd5Value, fileMd5),false);
        AfsFileInfo afsFileInfo = fileInfoService.getOne(Wrappers.<AfsFileInfo>lambdaQuery().eq(AfsFileInfo::getFileMd5, fileMd5),false);
        File storeFile  = null;
        try {
            if (afsFileStore != null && afsFileInfo != null && !ProcessStatus.TO_OSS.getCode().equalsIgnoreCase(afsFileStore.getProcessStatus())) {
                String filePath = fileProperties.getBasePath() + File.separator + afsFileStore.getFilePath();
                storeFile = new File(filePath);
                if (storeFile.exists()) {
                    HttpResponse httpResponse =  HttpUtil.createPost(MessageFormat.format(URL_TEMPLATE,gwtFastdfsProperties.getUrl(),fileMd5,gwtFastdfsProperties.getSceneId(),""))
                            .form("file",storeFile, afsFileInfo.getFileName())
                            .contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
                            .header("Authorization", TOKEN_HOLDER.getToken(gwtFastdfsProperties.getUrl(),gwtFastdfsProperties.getClientId(),gwtFastdfsProperties.getClientSecret(),gwtFastdfsProperties.getGrantType()))
                            .execute();
                    if(httpResponse.getStatus()!=200){
                        throw  new AfsBaseException("文件上传失败");
                    }
                    JSONObject resultJson = JSONObject.parseObject(httpResponse.bodyBytes(),JSONObject.class);
                    Boolean requestOK = resultJson.containsKey("success") && resultJson.getInteger("success") == ResultCodeEnum.SUCCESS.getCode();
                    String message = resultJson.containsKey("message") ? resultJson.getString("message") : "";
                    if (requestOK) {
                        JSONObject resultData = resultJson.containsKey("data") ? resultJson.getJSONObject("data") : new JSONObject();
                        Boolean uploadOK = resultData.containsKey("success") && resultData.getBoolean("success");
                        if (uploadOK) {
                            message = resultData.containsKey("message") ? resultData.getString("message") : "";
                            log.info("上传文件到OSS: " + message);
                            afsFileStore.setProcessStatus(ProcessStatus.TO_OSS.getCode());
                            ifileStoreService.updateById(afsFileStore);
                            storeFile.delete();
                        } else {
                            log.error("上传文件到OSS：上传失败" + message);
                            throw  new AfsBaseException("文件上传失败");
                        }
                    } else {
                        log.error("上传文件到OSS：请求失败" + message);
                        throw  new AfsBaseException("文件上传失败");
                    }
                }
            }
        }catch (Throwable e){
            log.error("上传oss失败",e);
            //上传失败状态重置为上传中
            afsFileStore.setProcessStatus(ProcessStatus.UPLOADING.getCode());
            ifileStoreService.updateById(afsFileStore);
            throw  new AfsBaseException("文件上传失败");
        }finally {
            if(storeFile!=null&&storeFile.exists()){
                storeFile.delete();
            }
        }
    }

    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    }

    private static class TokenHolder{
        private  String token;
        private long expireTime=0;
        private static Lock lock = new ReentrantLock();
        public  String getToken(String url,String clientId,String clientSecret,String grantType){
            if(StringUtils.isEmpty(this.token)||(expireTime-System.currentTimeMillis())<10000){
                lock.lock();
                try{
                    HttpResponse httpResponse =  HttpUtil.createPost(url+"/connect/token")
                            .form("client_id",clientId)
                            .form("client_secret",clientSecret)
                            .form(CommonConstants.GRANT_TYPE,grantType)
                            .execute();
                    if(httpResponse.getStatus()!=200){
                        throw new AfsBaseException("token  获取失败");
                    }
                    JSONObject jsonObject = JSONObject.parseObject(httpResponse.bodyBytes(),JSONObject.class);
                    if(!jsonObject.containsKey("access_token")){
                        throw new AfsBaseException("token  获取失败");
                    }
                    this.token = jsonObject.getString("token_type")+" "+jsonObject.getString("access_token");
                    this.expireTime=System.currentTimeMillis()+jsonObject.getInteger("expires_in")*1000;
                }finally {
                    lock.unlock();
                }
            }
            return  this.token;
        }
    }
}
